using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Runtime.Remoting;
using System.IO;
using System.Xml;
using System.Windows.Forms;
using System.Diagnostics;
using System.Collections;
using PluginManager.Events;

namespace PluginManager
{
    /// <summary>
    /// This is the core of the Plug-in engine. It provides support for dynamically loading and unloading
    /// plugins and protocols.  All public eventsare heard by the core and 
    /// plugins can add listeners to them with only a reference to GPCore.
    /// </summary>
    public static class Core
    {
        #region Members
        private static PluginManager plugman;
        private static Dictionary<string, string> commands;
        /// <summary>
        /// Gets the plugin manager.
        /// </summary>
        public static PluginManager PluginManager
        {
            get { return plugman; }
        }
        private static List<CoreEventListener> listeners;
        /// <summary>
        /// Gets a list of active plugins (anything that does not provide protocol
        /// support but listens to events from the core).
        /// </summary>
        public static List<CoreEventListener> Listeners
        {
            get { return listeners; }
        }

        public static Dictionary<string, string> Commands
        {
            get { return commands; }
        }

        #endregion

        /// <summary>
        /// Starts the PluginManager and the Application
        /// </summary>
        public static void Start()
        {
            plugman = new PluginManager("lib");
            listeners = new List<CoreEventListener>();
            commands = new Dictionary<string, string>();
            plugman.LoadUserAssemblies();
            LoadAllPlugins();
            //Application.Run();

        }

        public static void ExecuteCommand(string command, string[] parameters)
        {
            if(!Commands.ContainsKey(command))
            {
                Console.WriteLine("Command "+command+" not registered!");
                return;
            }
            foreach (CoreEventListener listener in Listeners)
            {
                if(listener.GetType().FullName == Commands[command])
                {
                    listener.onEvent(null,"OnCommand", new CommandEventArgs(command,parameters));
                }
            }
        }

        public static void RegisterCommands(object sender, string[] commands)
        {
            foreach (string command in commands)
            {
                RegisterCommand(sender, command);
            }
        }
        public static void RegisterCommand(object sender, string command)
        {
            Commands.Add(command, sender.GetType().FullName);
        }

        public static void Event(object sender, string name, EventArgs e)
        {
            foreach (CoreEventListener cel in listeners)
            {
                cel.onEvent(sender, name, e);
            }
        }
        
        private static void LoadAllPlugins()
        {
            foreach (String s in Core.PluginManager.Listeners)
            {
                CreateInstance(s);
            }
            //try
            //{
            //    StreamReader sr = new StreamReader(PluginManager.pluginFile);
            //    while (!sr.EndOfStream)
            //    {
            //        CreateInstance(sr.ReadLine());
            //    }
            //    sr.Close();
            //}
            //catch (FileNotFoundException)
            //{
            //    if (plugman.Listeners.Length > 0)
            //    {
            //        frmPlugin f = new frmPlugin();
            //        f.Show();
            //    }
            //}
        }

        #region Create and Destroy Instances
        /// <summary>
        /// Creates an instance of the given type and adds it to the 
        /// Listeners list if it is a <see cref="CoreEventListener"/>
        /// </summary>
        /// <example>
        /// Creating a <see cref="CoreEventListener" />
        /// <code>
        /// void MyMethod()
        /// {
        ///     GPBuddyList list = Core.CreateInstance("GPBuddyList.GPBuddyList") as GPBuddyList;
        /// }
        /// </code>
        /// </example>
        /// <param name="TypeName">The full name of the type to create an instance of.</param>
        /// <returns>A reference to the instance.</returns>
        public static object CreateInstance(string TypeName)
        {
            object o = plugman.CreateInstance(TypeName);

            AddEventHandlers(o);
            if (o is CoreEventListener)
            {
                CoreEventListener p = (o as CoreEventListener);
                Listeners.Add(p);
                p.onEvent(null,"OnLoad", new EventArgs());
            }
            return o;
        }

        private static void AddEventHandlers(object o)
        {
            foreach (EventInfo ev in o.GetType().GetEvents())
            {
                ev.AddEventHandler(o, Delegate.CreateDelegate(ev.EventHandlerType, typeof(Core), "Event"));
            }
        }

        /// <summary>
        /// Calls the destructor for the given plugin and removes it from the Listeners list.
        /// </summary>
        /// <param name="listener">The FullName of the Type of listener you wish to destroy</param>
        /// <example>
        /// Closing the Buddylist Plugin
        /// <code>
        /// void CloseBuddyList()
        /// {
        ///     Core.DestroyInstance(typeof(GPBuddyList).FullName);
        /// }
        /// </code>
        /// </example>
        public static void DestroyInstance(string listener)
        {
            for (int i = 0; i < Listeners.Count; i++)
            {
                if (Listeners[i].GetType().FullName == listener)
                {
                    Listeners[i].onEvent(null,"OnUnload", new EventArgs());
                    Listeners[i].Dispose();
                    Listeners[i] = null;
                    Listeners.RemoveAt(i);
                }
            }

        }
        /// <summary>
        /// Calls the destructor for the given plugin and removes it from the Listeners list.
        /// </summary>
        /// <param name="listener">The Type of Listener you wish to destroy</param>
        /// <example>
        /// <code>
        /// void Close()
        /// {
        ///     Core.DestroyInstance(typeof(GPBuddyList));
        /// }
        /// </code>
        /// </example>
        public static void DestroyInstance(Type listener) { DestroyInstance(listener.FullName); }
        /// <summary>
        /// Calls the destructor for the given plugin and removes it from the Listeners list.
        /// </summary>
        public static void DestroyInstance(CoreEventListener listener) { DestroyInstance(listener.GetType().FullName); }
        #endregion

        /// <summary>
        /// Displays a list of the available plugins.
        /// </summary>
        //public static void ShowPluginForm()
        //{
        //    frmPlugin f = new frmPlugin();
        //    f.Show();
        //}
        /// <summary>
        /// Set to true when Closing down Core, use this so you can be
        /// sure that you are not trying to close it down twice
        /// </summary>
        public static bool Closing = false;
        /// <summary>
        /// Closes the application.
        /// </summary>
        public static void Close()
        {
            if (Closing) return;
            Closing = true;
            foreach (CoreEventListener cel in Listeners)
            {
                try
                {
                    cel.onEvent(null,"OnUnload",new EventArgs());
                    cel.Dispose();
                }
                catch { }
            }
            plugman.Unload();
            Application.Exit();
        }
    }

    
    /// <summary>
    /// The Delegate to use if you want to throw an Event to Core
    /// </summary>
    /// <param name="sender">The Object sending the event</param>
    /// <param name="name">The name of the Method used to capture the Event</param>
    /// <param name="e">A Wrapper for the arguements for this Event</param>
    /// <remarks>
    /// The name paramater follows the format of on[eventname] without Event at 
    /// the end.
    /// </remarks>
    /// <example>
    /// To Throw the onMessageRecievedEvent
    /// <code>
    /// void GotAMessage(string sender, string msg)
    /// {
    ///     if (onMessageRecievedEvent != null)
    ///     {
    ///         onMessageRecievedEvent(this,"onMessageRecieved",new MessageArgs(sender,msg));
    ///     }
    /// }
    /// </code>
    /// </example>
    public delegate void NotifyCore(object sender, string name, EventArgs e);
}